using System;
using System.Runtime.Serialization;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Linq;

namespace ICalendar.CalendarProperties
{
    public abstract class CalendarPropertyBase<ValueType> : IXParameters, IXmlSerializable,  ISerializable
    {
        protected CalendarPropertyBase()
        {
            CustomXParameters = new XParameterCollection();
        }

        protected CalendarPropertyBase(ValueType value)
        {
            this.Value = value;
        }

        public virtual ValueType Value { get; set; }
        
        // protected abstract string GetElementName();
        protected abstract void SetValue(string value);
        protected abstract string GetValue();
        protected abstract void SetParametersFromXParameters(XParameterCollection xparams);
        protected abstract XParameterCollection GetXParametersFromParameters();
        protected abstract string[] GetXReservedParametersNames();

        public XParameterCollection CustomXParameters { get; set; }
        
        #region ISerializable Members

        //public void GetObjectData(SerializationInfo info, StreamingContext context)
        //{
        //    throw new NotImplementedException();
        //}

        #endregion

        public override string ToString()
        {
            var attr = (XmlRootAttribute)Attribute.GetCustomAttribute(GetType(), typeof(XmlRootAttribute));
            //The value can be A URI, but now i'll use just a MailAddress Uri type      

            bool escaped = Type.GetTypeCode(typeof (ValueType)) == TypeCode.String;
            return Util.Write(attr.ElementName.ToUpperInvariant(), GetValue(), GetXParameters(), escaped);    
        }

        //public static T FromString<T>(string element) where T : CalendarPropertyBase
        //{
        //    var result = default(T);
        //    result.FromString(element);
        //    return result;
        //}

        protected void SetXParams(XParameterCollection xparams)
        {
            var reservedList = GetXReservedParametersNames();
            var reservedParameters = new XParameterCollection();

            foreach (var pair in xparams)
            { 
                if (reservedList != null && !reservedList.Contains(pair.Key))
                {
                    this.CustomXParameters[pair.Key] = pair.Value;
                }
                else
                {
                    reservedParameters.Add(pair.Key, pair.Value); 
                }                     
            }

            SetParametersFromXParameters(reservedParameters);
        }

        public XParameterCollection GetXParameters()
        {
            if (CustomXParameters == null)
                return null;

            var result = GetXParametersFromParameters();

            foreach (var pair in CustomXParameters)
            {
                if (!result.ContainsKey(pair.Key))
                    result.Add(pair.Key, pair.Value);
            }

            return result;
        }

        public void FromString(string element)
        {
            string propname, value;
            XParameterCollection xparams;
            Util.Read(element, out propname, out value, out xparams);
            SetValue(value);
            SetXParams(xparams);
        }        
        #region IXmlSerializable Members
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            var element =(XElement)XElement.ReadFrom(reader);
            SetValue(element.Value);
            var xparams = new XParameterCollection();
            xparams.SetXParams(element.Attributes().ToArray());
            SetXParams(xparams);                
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            if (writer.WriteState == System.Xml.WriteState.Element)
            {
                var attr = (XmlRootAttribute)Attribute.GetCustomAttribute(GetType(), typeof (XmlRootAttribute));
                var attributes = GetXParameters().GetXAttributes();
                foreach (var attribute in attributes)
                {
                    writer.WriteAttributeString(attribute.Name.LocalName, attribute.Name.NamespaceName, attribute.Value);
                }
                writer.WriteValue(GetValue());
            }
            else
            {
                var attr = (XmlRootAttribute)Attribute.GetCustomAttribute(GetType(), typeof (XmlRootAttribute));
                var attendee = new XElement(attr.ElementName, GetValue(), GetXParameters().GetXAttributes());
                attendee.WriteTo(writer);                
            }
        }

        #endregion



        //public static CalendarPropertyBase<T> operator = (CalendarPropertyBase<T> obj, T value)
        //{
        //} 

        public static implicit operator string(CalendarPropertyBase<ValueType> x)
        {
            return x != null ? x.ToString() : "";
        }

        //public static implicit operator CalendarPropertyBase<T> (string value)
        //{
        //    var result = default(CalendarPropertyBase<T>);
        //    result.FromString(value);
        //    return result;
        //}

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}